<template>
  <div v-if="!(hideWhenEmpty && inputDisabled && !fileList.length)">
    <slot name="label"></slot>
    <div class="attachment-box">
      <!-- 文件列表 -->
      <slot
        :file-list="fileList"
        :temp-file-list="tempFileList"
        :remove-file="removeFile"
        :open-upload="openUpload"
        :download="download"
        :preview="preview"
        :disabled="inputDisabled">
        <ElTable
          :data="tempFileList"
          class="file-table"
          :border="false"
          :empty-text="inputDisabled ? '暂无文件' : '暂无文件'">
          <TableColumn label="文件名" :sortable="false">
            <template #default="{ row }">
              <div class="file-name-cell">
                <i class="file-icon" :style="{ backgroundImage: `url(${getFileIcon(row)})` }"></i>
                <div class="file-name-wrapper">
                  <div class="file-name-column">
                    {{ row.original }}
                    <!--  <span class="file-name" @click="preview(row)">{{ row.fileName }}</span>-->
                  </div>
                  <slot name="tag" :row="row"></slot>
                </div>
              </div>
            </template>
          </TableColumn>
          <TableColumn
            v-if="fileTypeOptions.length > 0"
            prop="classify"
            align="center"
            width="100"
            label="附件类型"
            :sortable="false">
            <template #default="{ row }">
              {{ typeMap[row.classify] }}
            </template>
          </TableColumn>
          <TableColumn prop="size" label="文件大小" width="80" align="center" :sortable="false">
            <template #default="{ row }">
              <template v-if="row.status"> {{ Math.round(row.percentage) }}% </template>
              <template v-else>
                {{ row.fileSize | formatSize }}
              </template>
            </template>
          </TableColumn>
          <TableColumn prop="createBy" label="上传人" width="100" align="center" :sortable="false" />
          <TableColumn prop="createTime" label="上传时间" width="150" align="center" :sortable="false" />
          <TableColumn :width="!$scopedSlots.button ? 70 : 190" label="操作" align="center" :sortable="false">
            <template #default="{ row }">
              <template v-if="!row.status">
                <i class="el-icon-download" style="cursor: pointer;margin-right: 6px;" type="text" @click="download(row)"></i>
                <ElButton
                  v-if="!inputDisabled"
                  type="text"
                  size="mini"
                  class="attachment-box-delete-button"
                  style="font-size: 16px"
                  icon="el-icon-delete"
                  title="删除"
                  @click="removeFile(row)" />
                <slot name="button" :row="row"></slot>
              </template>
              <template v-else>
                <ElButton
                  type="text"
                  title="取消"
                  :disabled="false"
                  size="mini"
                  class="attachment-box-delete-button"
                  style="font-size: 16px"
                  icon="el-icon-close"
                  @click="abort(row)" />
              </template>
            </template>
          </TableColumn>
        </ElTable>
      </slot>
      <!-- 上传组件 -->
      <div v-if="!inputDisabled" class="upload-wrapper">
        <ElUpload
          v-show="!hideUpload"
          ref="upload"
          :action="uploadUrl"
          :show-file-list="false"
          :headers="getHeader()"
          :http-request="uploadFile"
          :auto-upload="autoUpload"
          :data="{ uniqueCode: innerUnId, classify: type, model: model || '', ...extraData }"
          :multiple="multiple"
          :before-upload="beforeUploadFile"
          :on-change="uploadStatusChange"
          :on-remove="onRemove"
          :limit="limit"
          :on-exceed="onExceed"
          drag
          :accept="accept">
          <slot :accept="accept" name="upload" :open-upload="openUpload">
            <div class="el-upload__text" style="margin-top: 20px">请将文件拖到此处，或<em>点击此处</em>上传。</div>
            <div v-if="accept !== '*/*' && showAccept" class="el-upload__tip">
              只能上传{{ accept.replace(/,/g, '/') }}文件
            </div>
          </slot>
        </ElUpload>
        <slot name="bottom" :accept="accept"></slot>
      </div>
    </div>
  </div>
</template>

<script>
  import { ACCEPT_CONFIG } from '@/components/uploader/js/config'
  import { getFileSuffix, guid, isFunction, isString } from '@web-utils/core'
  import { getFileIcon } from '@/util'
  import { getAttachments, getUploadUrl, getHeader, remove } from '@/api/attachment/attachment'
  import { openFile } from '@/api/file-view'
  import { invalidToken, upload } from '@/http'

  export default {
    name: 'Attachment',
    inject: {
      elForm: {
        default: ''
      },
      elFormItem: {
        default: ''
      }
    },
    props: {
      api: Function,
      unid: String,
      hideUpload: Boolean,
      type: {
        type: String,
        default: ''
      },
      action: {
        type: [String, Function]
      },
      limitSize: {
        type: Number,
        default: 20
      },
      limit: {
        type: Number,
        default: 10
      },
      disabled: Boolean,
      hideWhenEmpty: Boolean,
      accept: {
        type: String,
        default: ACCEPT_CONFIG.getAll().join(',')
      },
      showAccept: {
        type: Boolean,
        default: true
      },
      autoUpload: {
        type: Boolean,
        default: true
      },
      multiple: {
        type: Boolean,
        default: true
      },
      extraData: {
        type: Object,
        default: () => ({})
      },
      fileTypeOptions: {
        type: Array,
        default: () => []
      },
      value: {
        type: Array,
        default: () => []
      },
      watch: {
        type: Boolean,
        default: true
      },
      model: String
    },
    data() {
      return {
        fileList: [],
        innerUnId: this.unid || '',
        tempFileList: []
      }
    },
    computed: {
      uploadUrl() {
        if (this.action) {
          if (isString(this.action)) {
            return this.action
          } else if (isFunction(this.action)) {
            return this.action()
          } else {
            return ''
          }
        } else {
          return getUploadUrl()
        }
      },
      inputDisabled() {
        return this.disabled || (this.elForm || {}).disabled
      },
      typeMap() {
        const map = {}
        if (this.fileTypeOptions?.length) {
          this.fileTypeOptions.forEach(value => {
            map[value.value] = value.label || value.value
          })
        }
        return map
      },
      watcher() {
        return {
          innerUnId: this.innerUnId,
          type: this.type
        }
      }
    },
    watch: {
      unid: {
        immediate: true,
        async handler(newVal) {
          this.innerUnId = newVal
        }
      },
      watcher: {
        immediate: true,
        async handler() {
          if (this.watch) {
            await this.fetchAttachments()
          }
        }
      },
      value: {
        immediate: true,
        handler(newVal) {
          this.fileList = newVal
        }
      },
      fileList(newVal) {
        this.tempFileList = [...(newVal || [])]
        this.$emit('input', newVal)
        this.$emit('files-change', newVal)
      }
    },
    methods: {
      getFileIcon,
      getHeader,
      async uploadFile(obj) {
        try {
          const res = await upload(obj.action, obj.file, {
            headers: obj.headers,
            onprogress: e => this.updateUploadProgress(e, obj.file),
            data: obj.data
          })
          if (res.data.code === 0) {
            this.onFileUploadSuccess(res, obj.file)
          } else {
            await this.onFileUploadError(res)
            this.$emit('on-error', res)
          }
        } catch (e) {
          console.error(e)
          await this.onFileUploadError(e)
          this.$emit('on-error', e)
        }
        this.$emit('on-complete')
      },
      onExceed() {
        this.$message.info(`最多只能上传${this.limit}个文件`)
      },
      emitInput(val) {
        if (val) {
          this.$emit('input', val)
        } else {
          this.$emit('input', [])
        }
      },
      getFileList() {
        return this.fileList
      },
      onFileUploadSuccess(res, file) {
        const sysFile = res.data.data.sysFile
        const index = this.fileList.findIndex(value => value.id === file.uid)
        if (index > -1) {
          this.$set(this.fileList, index, sysFile)
        } else {
          this.fileList.unshift(sysFile)
        }
        this.elFormItem?.clearValidate?.()
        this.emitInput(this.fileList)
        this.$emit('upload-file-success', sysFile)
      },
      async onFileUploadError(error) {
        if (error?.status === 401) {
          await invalidToken(error)
          return
        }
        this.$message.error(error.message)
      },
      updateUploadProgress(event, file) {
        const row = this.tempFileList.find(value => value.id === file.uid)
        if (row) {
          row.percentage = event.percent
        }
      },
      onRemove(file) {
        this.tempFileList = this.tempFileList.filter(value => value.id !== (file.uid || file.id))
      },
      uploadStatusChange(file) {
        const { name: fileName, size: fileSize, uid: id, status, percentage } = file
        // ready uploading success fail
        if (file.status === 'ready') {
          this.tempFileList.unshift({
            fileName,
            fileSize,
            id,
            status,
            percentage,
            fileType: getFileSuffix(fileName),
            file
          })
        }
        if (file.status === 'fail') {
          this.tempFileList = this.tempFileList.filter(value => value.id !== id)
        }
      },
      async beforeUploadFile(file) {
        await this.fetchUnId()
        return new Promise((resolve, reject) => {
          this.$emit('before-upload-file', file)
          const ext = this.accept.split(',').includes(`.${getFileSuffix(file.name)?.toLowerCase()}`)
          if (this.accept !== '*/*' && !ext) {
            const message = this.showAccept
              ? `只能上传${this.accept.replace(/,/g, '/')}文件`
              : '不支持该后缀名的上传文件'
            this.$message.warning({ message, duration: 3000, showClose: true })
            reject(Error(message))
            return
          }

          if (file.size / (1024 * 1024) > this.limitSize) {
            const message = `上传文件不能超过${this.limitSize}M!`
            this.$message.warning({ message, duration: 3000, showClose: true })
            reject(Error(message))
            return
          }
          resolve(true)
        })
      },
      async removeFile(row) {
        try {
          await this.$confirm('你确定要删除该文件吗？', { type: 'warning' })
          this.$refs.upload.clearFiles()
          const res = await remove(row.id)
          if (res.data.code === 0) {
            this.fileList = this.fileList.filter(value => value !== row)
            this.elFormItem?.validate?.()
            this.emitInput(this.fileList)
            this.$emit('remove-file-success', row)
          }
        } catch (e) {}
      },
      async fetchUnId() {
        if (!this.innerUnId || this.innerUnId === '') {
          const unid = guid()
          this.$emit('unid-created', unid)
          this.innerUnId = unid
        }
      },
      async fetchAttachments() {
        if (this.innerUnId && this.innerUnId !== '') {
          const params = {}
          if (this.fileTypeOptions?.length) {
            params.categories = this.fileTypeOptions.map(value => value.value)
          } else {
            params.classify = this.type
          }
          let res
          if (this.api) {
            res = await this.api(this.innerUnId, params)
          } else {
            res = await getAttachments(this.innerUnId, params)
          }

          if (res.data.code === 0) {
            const { data } = res.data
            this.fileList = data.records || []
            this.emitInput(this.fileList)
            return
          } else {
            this.$message.error(res.message)
          }
        }
        this.fileList = []
        this.emitInput(this.fileList)
      },
      download(row) {
        this.$downBlobFile(`/admin/sys-file/${row.bucketName}/${row.fileName}`, this.searchForm, row.fileName)
      },
      abort(row) {
        this.$refs.upload.abort(row.file)
        this.tempFileList = this.tempFileList.filter(value => value.id !== row.id)
      },
      openUpload() {
        this.$refs.upload.$refs['upload-inner']?.handleClick()
      },
      async preview(row) {
        await openFile(row, { wordType: 'file', mode: 'view' })
      }
    }
  }
</script>

<style lang="scss">
  .attachment-box {
    overflow: hidden;
    background-color: #fff;
    border: 1px solid var(--border-color);
    border-radius: 4px;

    .attachment-box-delete-button {
      color: grey;

      &:hover {
        color: #f56c6c;
      }

      &.download:hover {
        color: var(--color-primary);
      }
    }

    .attachment-box-download-button {
      color: grey;

      &:hover {
        color: var(--color-primary);
      }
    }

    .file-name-cell {
      display: flex;
      align-items: center;

      .file-name-wrapper {
        @include flex-row;
        align-items: center;
        width: 100%;
        padding-right: 20px;
        overflow: hidden;
        text-overflow: ellipsis;

        .file-name-column {
          @include flex-item;
          @include ellipsis-line;
        }

        .file-name {
          cursor: pointer;

          &:hover {
            color: var(--color-primary);
            text-decoration: underline;
          }
        }
      }

      .file-icon {
        display: inline-block;
        width: 24px;
        min-width: 24px;
        height: 24px;
        margin-right: 6px;
        background-repeat: no-repeat;
      }
    }

    .el-upload__tip {
      padding-bottom: 10px;
    }

    .el-button.is-circle {
      padding: 4px;
    }

    .el-button--danger {
      display: none;
    }

    .el-table__row:hover {
      .el-button--danger {
        display: block;
      }
    }

    .upload-wrapper {
      text-align: center;

      .el-upload {
        width: 100%;
      }

      .el-upload-dragger {
        width: 100%;
        height: 100%;
        border: 0;
      }
    }
  }
</style>
